/*
 * @描述: 
 * @创建者: kilomChou
 * @Date: 2021-04-12 10:15:59
 * @最后编辑: kilomChou
 * @LastEditTime: 2021-10-27 10:24:47
 */

import IConn from "../../core/net/IConn";
import { Proto, ProtoVo } from "../../data/Proto";
import { Vec2 } from "../../core/vec2/Vec2";
import { GameEvent, GameEventDef } from "../event/GameEvent";
import Game from "../Game";
import { IEventListener } from "../../core/event/EventDispatcher";
import Player from "../player/Player";

/** 连接管理器 */
export default class ConnMgr {

    constructor(
        /** 游戏实例 */
        readonly game: Game,
        /** 连接 */
        readonly conn: IConn,
        /** 玩家 */
        readonly player: Player,
    ) {
        let { ed, timer } = game.global;

        player.snake.spawn();
        conn.send(Proto.S2C_SCREEN_FOCUS, { nodeId: player.snake.head.id });

        game.world.snakeMgr.snakeMap.forEach((snake, id) => {
            let vo = this._snakeDic[id] || (this._snakeDic[id] = {} as ProtoVo.ISNAKE);
            vo.id = id;
            vo.name = snake.name;
            vo.rgbs = snake.rgbs;
        })

        this._listeners.push(ed.on(GameEvent.SNAKE_SPAWN, snake => {
            if (snake == player.snake) {
                conn.send(Proto.S2C_SCREEN_FOCUS, { nodeId: snake.head.id });
            }
            let vo = this._snakeDic[snake.id] || (this._snakeDic[snake.id] = {} as ProtoVo.ISNAKE);
            vo.id = snake.id;
            vo.name = snake.name;
            vo.rgbs = snake.rgbs;
        }))

        conn.on(Proto.C2S_CTRL_TURN, data => {
            this.player.ctrlTurn(data.ang);
        })

        conn.on(Proto.C2S_CTRL_SPRINT, data => {
            this.player.ctrlSprint(data.isSprint);
        })

        this.sendSnake();
        this.sendFood();

        timer.set(key_sendScoreRank, 1);
    }

    /** 蛇字典 */
    private _snakeDic: { [id: number]: ProtoVo.ISNAKE } = {}
    /** 可见食物集合 */
    private _screenFoodSet = new Set<number>();
    /** 可见蛇节点集合 */
    private _screenNodeSet = new Set<number>();
    /** 事件监听数组 */
    private _listeners: IEventListener<GameEventDef>[] = [];


    /** 断开连接 */
    disConn() {
        //移除蛇
        this.game.world.playerMgr.removePlayer(this.player);
        //取消监听事件
        this._listeners.forEach(l => l.off());
    }

    /** 每帧循环 */
    update() {

        this.sendFood();
        this.sendSnake();
        this.game.global.timer.check(key_sendScoreRank, () => this.sendScoreRank());
    }

    /** 发送蛇数据包 */
    sendSnake() {
        let { loadNodeMap, unloadNodeIdSet } = this.player.updateScreenNodeMap(this._screenNodeSet)

        loadNodeMap.forEach((nodes, snake) => {
            let updateSnake = this._snakeDic[snake.id] || {} as ProtoVo.ISNAKE;
            let updateNodes: ProtoVo.ISNAKE_NODE[] = []
            updateSnake.id = snake.id;
            updateSnake.w = Math.floor(snake.getWidth());
            updateSnake.nodes = updateNodes;
            nodes.forEach(node => {
                updateNodes.push({
                    id: node.id,
                    x: node.x,
                    y: node.y,
                    ang: Vec2.ang360(node.ang),
                    idx: node.idx
                })
            })
        })
        let updateSnakes = [];
        let unloadNodeIds = Array.from(unloadNodeIdSet);
        for (let key in this._snakeDic) {
            let snake = this._snakeDic[key];
            if (snake.nodes) {
                snake.nodes = snake.nodes.filter(node => !unloadNodeIdSet.has(node.id))
            }
            updateSnakes.push(snake);
        }
        updateSnakes.length && this.conn.send(Proto.S2C_SNAKE_UPDATE, { snakes: updateSnakes });
        unloadNodeIds.length && this.conn.send(Proto.S2C_SNAKE_NODE_UNLOAD, { nodeIds: unloadNodeIds });
    }

    /** 发送食物数据包 */
    sendFood() {
        let { loadFoods: _loadFoods, unloadFoodMap } = this.player.updateScreenFoodMap(this._screenFoodSet)

        let updateFoods: ProtoVo.IS2C_FOOD_UPDATE_FOOD[] = []
        let unloadFoodIds: ProtoVo.IS2C_FOOD_UNLOAD_FOODID[] = []

        _loadFoods.forEach(food => {
            updateFoods.push({
                id: food.id,
                x: food.x,
                y: food.y,
                w: food.w,
                rgb: food.color ? food.rgb : undefined
            })
        })

        unloadFoodMap.forEach((eaterId, foodId) => {
            unloadFoodIds.push({
                id: foodId,
                eaterId
            })
        })

        updateFoods.length && this.conn.send(Proto.S2C_FOOD_UPDATE, { foods: updateFoods })
        unloadFoodIds.length && this.conn.send(Proto.S2C_FOOD_UNLOAD, { foodIds: unloadFoodIds })
    }

    /** 发送积分排行包 */
    sendScoreRank() {
        let snakes = this.game.world.snakeMgr.getSortScoreSnakes();
        let rank: ProtoVo.IS2C_SCORE_RANK_SNAKE[] = [];
        for (let i = 0; i < 10; i++) {
            let snake = snakes[i];
            if (snake) {
                rank.push({
                    id: snake.id,
                    score: Math.floor(snake.score)
                })
            }
        }
        this.conn.send(Proto.S2C_SCORE_RANK, { rank });
    }

}

/** 发送积分排行包的定时器key */
const key_sendScoreRank = "connMgr_sendScoreRank"